/**************************************************************************/
/*                                                                        */
/*       Partial Copyright (c) Microsoft Corporation. All rights reserved.*/
/*                                                                        */
/*       This software is licensed under the Microsoft Software License   */
/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
/*       and in the root directory of this software.                      */
/*      Partial Copyright (c) STMicroelctronics 2020. All rights reserved */
/**************************************************************************/

/* Include necessary system files.  */
#include <string.h>

#include "usbh_user.h"
#include "bsp.h"
#include "tx_api.h"
#include "fx_api.h"
#include "usb_core.h"
#include "usbh_msc_class.h"
#include "common.h"	  	

/* Handle for USB Host */
extern otg_core_type otg_core_struct;

UINT  _fx_partition_offset_calculate(void  *partition_sector, UINT partition, ULONG *partition_start, ULONG *partition_size);

static uint8_t sector_read(void *buff, uint32_t sec_site, uint32_t amount);
static uint8_t sector_write(void *buff, uint32_t sec_site, uint32_t amount);


/**
  * @brief This function is the entry point to the STM32 SDIO disk driver.     */
/*        It relies on the STM32 peripheral library from ST.
 * @param None
 * @retval None
 */
VOID  fx_udisk_driver(FX_MEDIA *media_ptr)
{
    int32_t status;
    ULONG       partition_start;
    ULONG       partition_size;

    /* before performing any operation, check the status of the SDMMC */
    if(usbh_msc_is_ready(&otg_core_struct.host, 0) != MSC_OK)
    {
        media_ptr->fx_media_driver_status = FX_IO_ERROR;
        return;
    }

    /* Process the driver request specified in the media control block.  */
    switch (media_ptr->fx_media_driver_request)
    {
        case FX_DRIVER_INIT:
        {
            if(usbh_msc_is_ready(&otg_core_struct.host, 0) == MSC_OK)
            {
                media_ptr->fx_media_driver_status = FX_SUCCESS;
            }
            else
            {
                media_ptr->fx_media_driver_status = FX_IO_ERROR;
            }

            break;
        }

        case FX_DRIVER_UNINIT:
        {
            /* Successful driver request.  */
            media_ptr->fx_media_driver_status = FX_SUCCESS;
            break;
        }

        case FX_DRIVER_READ:
        {
            status = sector_read(
                media_ptr->fx_media_driver_buffer,
                media_ptr->fx_media_driver_logical_sector + media_ptr->fx_media_hidden_sectors,
                media_ptr->fx_media_driver_sectors
                );

            if (status == 0)
                media_ptr->fx_media_driver_status = FX_SUCCESS;
            else
                media_ptr->fx_media_driver_status = FX_IO_ERROR;

            break;
        }

        case FX_DRIVER_WRITE:
        {
            status = sector_write(
                media_ptr->fx_media_driver_buffer,
                media_ptr->fx_media_driver_logical_sector + media_ptr->fx_media_hidden_sectors,
                media_ptr->fx_media_driver_sectors
                );


            if (status == 0)
                media_ptr->fx_media_driver_status = FX_SUCCESS;
            else
                media_ptr->fx_media_driver_status = FX_IO_ERROR;

            break;
        }

        case FX_DRIVER_FLUSH:
        {
            /* Return driver success.  */
            media_ptr->fx_media_driver_status = FX_SUCCESS;
            break;
        }

        case FX_DRIVER_ABORT:
        {
            /* Return driver success.  */
            media_ptr->fx_media_driver_status = FX_SUCCESS;
            break;
        }

        case FX_DRIVER_BOOT_READ:
        {
            status = sector_read(
                media_ptr->fx_media_driver_buffer,
                0,
                1);


            if (status == 0)
            {
                media_ptr->fx_media_driver_status = FX_SUCCESS;
            }
            else
            {
                media_ptr->fx_media_driver_status = FX_IO_ERROR;
                break;
            }

            /* Check if the sector 0 is the actual boot sector, otherwise calculate the offset into it.
            Please note that this should belong to higher level of MW to do this check and it is here
            as a temporary work solution */

            partition_start = 0;

            status = _fx_partition_offset_calculate(media_ptr->fx_media_driver_buffer, 0,
                &partition_start, &partition_size);

            /* Check partition read error.  */
            if (status)
            {
                /* Unsuccessful driver request.  */
                media_ptr->fx_media_driver_status = FX_IO_ERROR;
                return;
            }

            /* Now determine if there is a partition...   */
            if (partition_start)
            {
                /* Yes, now lets read the actual boot record.  */
                status = sector_read(
                    media_ptr->fx_media_driver_buffer,
                    partition_start,
                    1);

                /* Check status of SDIO Read.  */
                if (status != 0)
                {

                    /* Unsuccessful driver request.  */
                    media_ptr->fx_media_driver_status = FX_IO_ERROR;
                    return;
                }
            }

            /* Successful driver request.  */
            media_ptr->fx_media_driver_status = FX_SUCCESS;
            break;
        }
        case FX_DRIVER_BOOT_WRITE:
        {
            status = sector_write(
                media_ptr->fx_media_driver_buffer,
                0,
                1
                );

            if (status == 0)
                media_ptr->fx_media_driver_status = FX_SUCCESS;
            else
                media_ptr->fx_media_driver_status = FX_IO_ERROR;

            break;
        }
        default:
        {
            media_ptr->fx_media_driver_status = FX_IO_ERROR;
            break;
        }
    }
}

uint8_t sector_read(void *buff, uint32_t sec_site, uint32_t count)
{
  usb_sts_type status;
  
  status = usbh_msc_read(&otg_core_struct.host, sec_site, count, buff, 0);
  
  if(status == USB_OK)
    return 0;
  
  return 1;
}

uint8_t sector_write(void *buff, uint32_t sec_site, uint32_t count)
{
  usb_sts_type status;

  status = usbh_msc_write(&otg_core_struct.host, sec_site, count, (uint8_t *)buff, 0);
  
  if(status == USB_OK)
    return 0;
  
  return 1;
}

